package main

import (
	"context"
	"fmt"
	"io"
	"log"
	"net/http"
	"net/url"
	"os"
	"time"
)

func main() {
	r := Search("golang")
	if r.Err != nil {
		log.Panic(r.Err)
	} else {
		defer r.Body.Close()
		fmt.Fprint(os.Stdout, r.Status)
		io.Copy(os.Stdout, r.Body)
	}
}

func Search(keyword string) *Result {

	bingUrl := baiduSearchUrl(keyword)
	sogoUrl := sogoSearchUrl(keyword)
	ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
	defer cancel()

	bingRequest, err := http.NewRequestWithContext(ctx, "GET", bingUrl.String(), nil)
	if err != nil {
		log.Panic(err)
	}

	sogoRequest, err := http.NewRequestWithContext(ctx, "GET", sogoUrl.String(), nil)
	if err != nil {
		log.Panic(err)
	}

	ch := make(chan *Result)
	go func() {
		ch <- NewResult(http.DefaultClient.Do(bingRequest))
	}()

	go func() {
		ch <- NewResult(http.DefaultClient.Do(sogoRequest))
	}()

	r := <-ch
	cancel()

	failure := <-ch
	if failure.Body != nil {
		io.Copy(io.Discard, failure.Body)
		failure.Body.Close()
	}
	return r
}

func bingSearchUrl(keyword string) *url.URL {
	u, err := url.Parse("https://cn.bing.com/search")
	if err != nil {
		log.Panic(err)
	}
	q := make(url.Values)
	q.Add("q", keyword)
	u.RawQuery = q.Encode()
	return u
}

func baiduSearchUrl(keyword string) *url.URL {
	u, err := url.Parse("https://www.baidu.com/s")
	if err != nil {
		log.Panic(err)
	}
	q := make(url.Values)
	q.Add("wd", keyword)
	u.RawQuery = q.Encode()
	return u
}

func sogoSearchUrl(keyword string) *url.URL {
	u, err := url.Parse("https://www.sogou.com/web")
	if err != nil {
		log.Panic(err)
	}
	q := make(url.Values)
	q.Add("query", keyword)
	u.RawQuery = q.Encode()
	return u
}

type Result struct {
	Source string
	Status string
	Body   io.ReadCloser
	Err    error
}

func NewResult(r *http.Response, err error) *Result {
	if err != nil {
		return &Result{
			Err: err,
		}
	}
	return &Result{
		Source: r.Request.URL.Host,
		Status: r.Status,
		Body:   r.Body,
	}
}
